{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Over Current Relay (OC relay)\n",
    "\n",
    "Pandapower supports running protection scenarios using Definite Time Over Current Relay (DTOC), Inverse Definite Minimum Time (IDMT) and Inverse Definite Time Minimum over current Relay, which is a combination of DTOC and IDMT (IDTOC) in radial and meshed networks. It supports the automated time grading plan based on grid searches and manual time grading."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example Network\n",
    "Here is a little example of creating the over-current protection scenario. First, we create a network with 7 buses and 5 switches. The switch is type defined as 'CB_DTOC' for running protection scenarios using DTOC relay, 'CB_IDMT' for using IDMT relay and 'CB_IDTOC' for using IDTOC relay.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:13.722009Z",
     "start_time": "2025-10-17T08:19:09.446142Z"
    }
   },
   "outputs": [],
   "source": [
    "from pandapower.create import (\n",
    "    create_empty_network,\n",
    "    create_buses,\n",
    "    create_ext_grid,\n",
    "    create_lines,\n",
    "    create_loads,\n",
    "    create_switches\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:13.752864Z",
     "start_time": "2025-10-17T08:19:13.731944Z"
    }
   },
   "outputs": [],
   "source": [
    "def oc_relay_net():\n",
    "    # create an empty network\n",
    "    net = create_empty_network()\n",
    "    \n",
    "    #create buses\n",
    "    create_buses(net, nr_buses=7, vn_kv=20, index=[0,1,2,3,4,5,6], name=None, type=\"n\",\n",
    "                    geodata=[(0,0), (0, -1), (-2, -2), (-2, -4), (2, -2), (2, -3), (2, -4)])\n",
    "\n",
    "    # create external grids\n",
    "    create_ext_grid(net, 0, vm_pu=1.0, va_degree=0, s_sc_max_mva=100, s_sc_min_mva=50, rx_max=0.1, rx_min=0.1)\n",
    "    \n",
    "    create_lines(net, from_buses=[0,1,2,1,4,5], to_buses=[1,2,3,4,5,6], length_km=[2,5,4,4,0.5,0.5],std_type=\"NAYY 4x50 SE\",\n",
    "                 name=None, index=[0,1,2,3,4,5], df=1., parallel=1)\n",
    "\n",
    "    net.line[\"endtemp_degree\"] = 250\n",
    "    \n",
    "    # Define switches\n",
    "    create_switches(net, buses =  [0,1,1,2,4,5], elements =\n",
    "                       [0,1,3,2,4,5], et = 'l', type =\"CB_DTOC\")\n",
    "    #define load\n",
    "    create_loads(net, buses=[3,6], p_mw=[5,2], q_mvar=[1,1], const_z_percent=0, const_i_percent=0, sn_mva=None,\n",
    "                     name=None, scaling=1., index=[0,1])\n",
    "\n",
    "    return net"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  OC Relay Class\n",
    "\n",
    "Users can create over-current relays in circuits by calling the OCRelay class on switches within a pandapower network. \n",
    "\n",
    "INPUT:\n",
    "\n",
    "        **net** (attrdict) - pandapower net\n",
    "\n",
    "        **switch_index** (int) - index of the switch that the relay acts upon\n",
    "\n",
    "        **oc_relay_type** (str) - string specifying the type of over-current protection. Must be either \"DTOC\", \"IDMT\",\n",
    "        or \"IDTOC\".\n",
    "\n",
    "        **time_settings** (list of DataFrame) - If given as a list, the time grading will be calculated based on\n",
    "        topological grid search, and manual tripping time can be provided as a dataframe by the respective column names.\n",
    "\n",
    "                For DTOC:\n",
    "                time_settings =[t>>, t>, t_diff] or Dataframe columns as 'switch_id', 't_gg', 't_g'\n",
    "\n",
    "                - t>> (t_gg): instantaneous tripping time in seconds\n",
    "                - t> (t_g):  primary backup tripping time in seconds,\n",
    "                - t_diff: time grading delay difference in seconds\n",
    "\n",
    "\n",
    "                For IDMT:\n",
    "                time_settings =[tms, t_grade] or Dataframe columns as 'switch_id', 'tms', 't_grade'\n",
    "\n",
    "                - tms: time multiplier settings in seconds\n",
    "                - t_grade:  time grading delay difference in seconds\n",
    "\n",
    "                For IDTOC:\n",
    "                time_settings =[t>>, t>, t_diff, tms,t_grade] or Dataframe columns as 'switch_id', 't_gg', 't_g','tms', 't_grade'\n",
    "\n",
    "                - t>> (t_gg): instantaneous tripping time in seconds\n",
    "                - t> (t_g):  primary backup tripping time in seconds,\n",
    "                - t_diff: time grading delay difference in seconds\n",
    "                - tms: time multiplier settings in seconds\n",
    "                - t_grade:  time grading delay difference in seconds\n",
    "\n",
    "        **overload_factor** - (float, 1.25)- Allowable overloading on the line used to calculate the pick-up current\n",
    "\n",
    "        **ct_current_factor** -(float, 1.2) - Current multiplication factor to calculate the pick-up current\n",
    "\n",
    "        **safety_factor** -(float, 1) - Safety limit for the instantaneous pick-up current\n",
    "\n",
    "        **inverse_overload_factor** -(float, 1.2)- Allowable inverse overloading to define the pick-up current in IDMT\n",
    "        relay\n",
    "\n",
    "        **pickup_current_manual** - (DataFrame, None) - User-defined relay trip currents can be given as a dataframe.\n",
    "\n",
    "                DTOC: Dataframe with columns as 'switch_id', 'I_gg', 'I_g'\n",
    "\n",
    "                IDMT: Dataframe with columns as 'switch_id', 'I_s'\n",
    "\n",
    "                IDTOC: Dataframe with columns as 'switch_id', 'I_gg', 'I_g', 'I_s'\n",
    "\n",
    "        **in_service** (bool, True) - specify whether relay is in service\n",
    "\n",
    "        **overwrite** (bool, True) - specify whether this oc relay should overwrite existing protection devices acting\n",
    "        on the switch\n",
    "\n",
    "        **sc_fraction** (float, 0.95) - Maximum possible extent to which the short circuit can be created on the line\n",
    "\n",
    "        **curve_type** (str, 'standard_inverse') - specify the type of curve used in IDMT and IDTOC.\n",
    "\n",
    "            Curve type can be:\n",
    "                 - 'standard_inverse'\n",
    "                 - 'very_inverse',\n",
    "                 - 'extremely_inverse',\n",
    "                 - 'long_inverse'\n",
    "                 \n",
    "## DTOC Relay\n",
    "\n",
    "We will now create DTOC relays at each switch in the network. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:16.658799Z",
     "start_time": "2025-10-17T08:19:13.825495Z"
    }
   },
   "outputs": [],
   "source": [
    "net = oc_relay_net()\n",
    "from pandapower.protection.protection_devices.ocrelay import OCRelay\n",
    "\n",
    "for k in range(6):\n",
    "    OCRelay(net, switch_index=k, oc_relay_type='DTOC', time_settings=[0.07, 0.5, 0.3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To run a fault scenario on a line, create a short circuit at the desired location by calling create_sc_bus(). Then, call calc_sc() to perform the short circuit calculation. For line faults, the bus argument of calc_sc() should be the highest bus index because create_sc_bus() inserts a virtual bus at the desired location on the line and appends a bus index to the network. When calling calc_sc(), make sure to specify branch_results=True. \n",
    "\n",
    "To see how the over-current relays respond, call calculate_protection_times(). For faults, the scenario should be 'sc'. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:16.814385Z",
     "start_time": "2025-10-17T08:19:16.676453Z"
    }
   },
   "outputs": [],
   "source": [
    "from pandapower.protection.utility_functions import create_sc_bus\n",
    "import pandapower.shortcircuit as sc\n",
    "from pandapower.protection.run_protection import calculate_protection_times\n",
    "\n",
    "# calculate the short circuit\n",
    "net_sc = create_sc_bus(net, sc_line_id=4, sc_fraction=0.5)\n",
    "sc.calc_sc(net_sc, bus=max(net_sc.bus.index), branch_results=True)\n",
    "\n",
    "# calculate the protection times\n",
    "protection_results = calculate_protection_times(net_sc, scenario='sc')\n",
    "\n",
    "# show protection_results\n",
    "protection_results"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Show tripped grid\n",
    "\n",
    "Shows tripped switches, short circuit currents, sc location with respective annotations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:17.372094Z",
     "start_time": "2025-10-17T08:19:17.033761Z"
    }
   },
   "outputs": [],
   "source": [
    "from pandapower.protection.utility_functions import plot_tripped_grid_protection_device\n",
    "plot_tripped_grid_protection_device(net_sc, protection_results, sc_bus=max(net_sc.bus.index), sc_location=0.5)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create I-t Plot\n",
    "To view the time-current characteristic curve of an oc relay, call its plot_protection_characteristic() method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:18.616189Z",
     "start_time": "2025-10-17T08:19:17.507490Z"
    },
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "net.protection.object.at[4].plot_protection_characteristic(net=net, xmin=10, xmax=10000, ymin=0.01, ymax=10000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  IDMT Relay \n",
    "\n",
    "IDMT largely follow the same procedure as DTOC, with the main difference being the time_settings parameters. The example below will show the same steps as in DTOC."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:19.757481Z",
     "start_time": "2025-10-17T08:19:18.686666Z"
    }
   },
   "outputs": [],
   "source": [
    "# Change the switch type in the network\n",
    "\n",
    "net=oc_relay_net()\n",
    "net.switch.type='CB_IDMT'\n",
    "\n",
    "# create IDMT OC Relays\n",
    "for k in range(6):\n",
    "    OCRelay(net, switch_index=k, oc_relay_type='IDMT', time_settings=[1, 0.5])\n",
    "    \n",
    "# create short circuit\n",
    "net_sc = create_sc_bus(net, sc_line_id=4, sc_fraction=0.5)\n",
    "sc.calc_sc(net_sc, bus=max(net_sc.bus.index), branch_results=True)\n",
    "\n",
    "# find protection results\n",
    "protection_results = calculate_protection_times(net_sc, scenario='sc')\n",
    "\n",
    "# show protection_results\n",
    "protection_results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:20.162552Z",
     "start_time": "2025-10-17T08:19:19.849801Z"
    }
   },
   "outputs": [],
   "source": [
    "# show tripped grid\n",
    "plot_tripped_grid_protection_device(net_sc, protection_results, sc_bus=max(net_sc.bus.index), sc_location=0.5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:21.314303Z",
     "start_time": "2025-10-17T08:19:20.253711Z"
    }
   },
   "outputs": [],
   "source": [
    "# show time-current characteristic of oc relay at switch 4\n",
    "net.protection.object.at[4].plot_protection_characteristic(net=net, xmin=10, xmax=10000, ymin=0.01, ymax=10000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All the additional features and plotting functions are possible for IDMT relay (similar to DTOC)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  IDTOC Relay \n",
    "\n",
    "The procedure for IDTOC relays is very similar to DTOC and IDMT. Make sure to specify the time_settings appropriately. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:22.548543Z",
     "start_time": "2025-10-17T08:19:21.344136Z"
    }
   },
   "outputs": [],
   "source": [
    "# Change the switch type in the network\n",
    "net=oc_relay_net()\n",
    "net.switch.type='CB_IDTOC'\n",
    "\n",
    "# create IDTOC relays in the network\n",
    "for k in range(6):\n",
    "    OCRelay(net, switch_index=k, oc_relay_type='IDTOC', time_settings=[0.07, 0.5, 0.3, 1, 0.5])\n",
    "\n",
    "# create short circuit\n",
    "net_sc = create_sc_bus(net, sc_line_id=4, sc_fraction=0.5)\n",
    "sc.calc_sc(net_sc, bus=max(net_sc.bus.index), branch_results=True)\n",
    "protection_results = calculate_protection_times(net_sc, scenario='sc')\n",
    "                                                \n",
    "# find protection results\n",
    "protection_results = calculate_protection_times(net_sc, scenario='sc')\n",
    "\n",
    "# show protection_results\n",
    "protection_results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:22.953457Z",
     "start_time": "2025-10-17T08:19:22.748619Z"
    }
   },
   "outputs": [],
   "source": [
    "# show tripped grid\n",
    "plot_tripped_grid_protection_device(net_sc, protection_results, sc_bus=max(net_sc.bus.index), sc_location=0.5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-17T08:19:24.014270Z",
     "start_time": "2025-10-17T08:19:23.209902Z"
    }
   },
   "outputs": [],
   "source": [
    "# show time-current characteristic of oc relay at switch 4\n",
    "net.protection.object.at[4].plot_protection_characteristic(net=net, xmin=10, xmax=10000, ymin=0.01, ymax=10000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  All the additional features which are previously described will also function for IDTOC relay"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
